use super::super::types::Color;
use super::{bounds::Bounded, identities::Zero, num::Num, cast::NumCast};
use super::{Canvas, Pixel};
use std::fmt;
use std::marker::PhantomData;
use std::ops::{AddAssign, Deref, DerefMut, Range};
use std::slice::ChunksExactMut;

pub trait Primitive:
  Copy + NumCast + Num + PartialOrd<Self> + Clone + Bounded
{
  const DEFAULT_MAX_VALUE: Self;
  const DEFAULT_MIN_VALUE: Self;
}

macro_rules! declare_primitive {
  ($type:ty: ($from:expr)..$to:expr) => {
    impl Primitive for $type {
      const DEFAULT_MAX_VALUE: Self = $to;
      const DEFAULT_MIN_VALUE: Self = $from;
    }
  };
}

declare_primitive!(usize: (0)..Self::MAX);
declare_primitive!(u8: (0)..Self::MAX);
declare_primitive!(u16: (0)..Self::MAX);
declare_primitive!(u32: (0)..Self::MAX);
declare_primitive!(u64: (0)..Self::MAX);

declare_primitive!(isize: (Self::MIN)..Self::MAX);
declare_primitive!(i8: (Self::MIN)..Self::MAX);
declare_primitive!(i16: (Self::MIN)..Self::MAX);
declare_primitive!(i32: (Self::MIN)..Self::MAX);
declare_primitive!(i64: (Self::MIN)..Self::MAX);

declare_primitive!(f32: (0.0)..1.0);
declare_primitive!(f64: (0.0)..1.0);

pub trait ImagePixel: Copy + Clone {
  type Subpixel: Primitive;
  const CHANNEL_COUNT: u8;
  fn channels(&self) -> &[Self::Subpixel];
  fn channels_mut(&mut self) -> &mut [Self::Subpixel];
  fn from_slice_mut(slice: &mut [Self::Subpixel]) -> &mut Self;

  const COLOR_MODEL: &'static str;
}

#[derive(Debug, Hash, PartialEq, Eq)]
pub struct ImageBuffer<P: ImagePixel, Container> {
  width: u32,
  height: u32,
  _phantom: PhantomData<P>,
  data: Container,
}

impl<P, Container> ImageBuffer<P, Container>
where
  P: ImagePixel,
  Container: Deref<Target = [P::Subpixel]>,
{
  fn image_buffer_len(width: u32, height: u32) -> Option<usize> {
    Some(<P as ImagePixel>::CHANNEL_COUNT as usize)
      .and_then(|size| size.checked_mul(width as usize))
      .and_then(|size| size.checked_mul(height as usize))
  }

  fn pixel_indices(&self, x: u32, y: u32) -> Option<Range<usize>> {
    if x >= self.width || y >= self.height {
      return None;
    }
    Some(self.pixel_indices_unchecked(x, y))
  }

  fn pixel_indices_unchecked(&self, x: u32, y: u32) -> Range<usize> {
    let no_channels = <P as ImagePixel>::CHANNEL_COUNT as usize;
    let min_index =
      (y as usize * self.width as usize + x as usize) * no_channels;
    min_index..min_index + no_channels
  }

  pub fn into_raw(self) -> Container {
    self.data
  }

  pub fn width(&self) -> u32 {
    self.width
  }

  pub fn height(&self) -> u32 {
    self.height
  }

  pub fn dimensions(&self) -> (u32, u32) {
    (self.width, self.height)
  }
}

pub struct PixelsMut<'a, P: ImagePixel + 'a>
where
  P::Subpixel: 'a,
{
  chunks: ChunksExactMut<'a, P::Subpixel>,
}

impl<'a, P: ImagePixel + 'a> Iterator for PixelsMut<'a, P>
where
  P::Subpixel: 'a,
{
  type Item = &'a mut P;
  #[inline]
  fn next(&mut self) -> Option<Self::Item> {
    self
      .chunks
      .next()
      .map(|v| <P as ImagePixel>::from_slice_mut(v))
  }
}

impl<P, Container> ImageBuffer<P, Container>
where
  P: ImagePixel,
  Container: Deref<Target = [P::Subpixel]> + DerefMut,
{
  pub fn inner_pixels_mut(&mut self) -> &mut [P::Subpixel] {
    let len = Self::image_buffer_len(self.width, self.height).unwrap();
    &mut self.data[..len]
  }

  pub fn pixels_mut(&mut self) -> PixelsMut<P> {
    PixelsMut {
      chunks: self
        .inner_pixels_mut()
        .chunks_exact_mut(<P as ImagePixel>::CHANNEL_COUNT as usize),
    }
  }

  pub fn get_pixel_mut(&mut self, x: u32, y: u32) -> &mut P {
    match self.pixel_indices(x, y) {
      None => panic!(
        "Image index {:?} out of bounds {:?}",
        (x, y),
        (self.width, self.height)
      ),
      Some(pixel_indice) => {
        <P as ImagePixel>::from_slice_mut(&mut self.data[pixel_indice])
      }
    }
  }

  pub fn put_pixel(&mut self, x: u32, y: u32, pixel: P) {
    *self.get_pixel_mut(x, y) = pixel
  }
}

impl<P: ImagePixel> ImageBuffer<P, Vec<P::Subpixel>> {
  pub fn new(width: u32, height: u32) -> ImageBuffer<P, Vec<P::Subpixel>> {
    let size = Self::image_buffer_len(width, height)
      .expect("Buffer length in `ImageBuffer::new` overflows usize");
    ImageBuffer {
      data: vec![Zero::zero(); size],
      width,
      height,
      _phantom: PhantomData,
    }
  }

  pub fn from_pixel(
    width: u32,
    height: u32,
    pixel: P,
  ) -> ImageBuffer<P, Vec<P::Subpixel>> {
    let mut buf = ImageBuffer::new(width, height);
    for p in buf.pixels_mut() {
      *p = pixel;
    }
    buf
  }
}

impl<P: ImagePixel> fmt::Debug for PixelsMut<'_, P>
where
  P::Subpixel: fmt::Debug,
{
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    f.debug_struct("PixelsMut")
      .field("chunks", &self.chunks)
      .finish()
  }
}

pub trait Enlargeable: Sized + Bounded + NumCast {
  type Larger: Copy
    + NumCast
    + Num
    + PartialOrd<Self::Larger>
    + Clone
    + Bounded
    + AddAssign;

  fn clamp_from(n: Self::Larger) -> Self {
    if n > Self::max_value().to_larger() {
      Self::max_value()
    } else if n < Self::min_value().to_larger() {
      Self::min_value()
    } else {
      NumCast::from(n).unwrap()
    }
  }

  fn to_larger(self) -> Self::Larger {
    NumCast::from(self).unwrap()
  }
}

impl Enlargeable for u8 {
  type Larger = u32;
}
impl Enlargeable for u16 {
  type Larger = u32;
}
impl Enlargeable for u32 {
  type Larger = u64;
}
impl Enlargeable for u64 {
  type Larger = u128;
}
impl Enlargeable for usize {
  type Larger = u128;
}
impl Enlargeable for i8 {
  type Larger = i32;
}
impl Enlargeable for i16 {
  type Larger = i32;
}
impl Enlargeable for i32 {
  type Larger = i64;
}
impl Enlargeable for i64 {
  type Larger = i128;
}
impl Enlargeable for isize {
  type Larger = i128;
}
impl Enlargeable for f32 {
  type Larger = f64;
}
impl Enlargeable for f64 {
  type Larger = f64;
}

#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
#[repr(C)]
pub struct Luma<T: Primitive>(pub [T; 1]);

impl<T: Primitive> ImagePixel for Luma<T> {
  type Subpixel = T;
  const CHANNEL_COUNT: u8 = 1;

  #[inline(always)]
  fn channels(&self) -> &[Self::Subpixel] {
    &self.0
  }

  #[inline(always)]
  fn channels_mut(&mut self) -> &mut [Self::Subpixel] {
    &mut self.0
  }

  const COLOR_MODEL: &'static str = "Y";

  fn from_slice_mut(slice: &mut [Self::Subpixel]) -> &mut Self {
    assert_eq!(slice.len(), 1);
    unsafe { &mut *(slice.as_mut_ptr() as *mut Self) }
  }
}

impl<S: Primitive + 'static> Pixel for Luma<S> {
  type Image = ImageBuffer<Self, Vec<S>>;
  type Canvas = (Self, Self::Image);
  fn default_color(color: Color) -> Self {
    match color.select(S::zero(), S::max_value()) {
      p => Luma([p]),
    }
  }
}

impl<P: ImagePixel + 'static> Canvas for (P, ImageBuffer<P, Vec<P::Subpixel>>) {
  type Pixel = P;
  type Image = ImageBuffer<P, Vec<P::Subpixel>>;

  fn new(
    width: u32,
    height: u32,
    dark_pixel: Self::Pixel,
    light_pixel: Self::Pixel,
  ) -> Self {
    (
      dark_pixel,
      ImageBuffer::from_pixel(width, height, light_pixel),
    )
  }

  fn draw_dark_pixel(&mut self, x: u32, y: u32) {
    self.1.put_pixel(x, y, self.0);
  }

  fn into_image(self) -> Self::Image {
    self.1
  }
}
